# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: leon, chensili

import concurrent.futures
import datetime
import json
import logging
import sys
import traceback

from absl import app, flags

from coin.base.param_util import to_list
from coin.base.datetime_util import (convert_string_to_dates, iterate_date)
from coin.exchange.kr_rest.product.product_impl \
    import get_exchange_product_list_grouped

from coin.strategy.mm.dumper_base import run_from_archive
from coin.support.feed_tool.feed_stats.logic.feed_stats_dumper \
    import DumpStrategy
from coin.strategy.mm.subscription import SubscriptionRequest
from coin.support.feed_tool.feed_stats.logic.util import (get_exchange_api_id_from_request,
                                                          fetch_git_commit_sha_datetime)
from coin.exchange.util.feed_checker import get_default_feed_checker_config
from coin.strategy.mm.subscription import FeedSubscriptionRequest

FLAGS = flags.FLAGS

EXCHANGE_LIST = [
    ('Spot', 'Binance', 'snapshot'),
    ('Spot', 'Binance', 'l1_realtime'),
    ('Spot', 'Bitfinex', None),
    ('Futures', 'Bitflyer', None),
    ('Futures', 'Bitmex', None),
    ('Spot', 'Bithumb', None),
    ('Spot', 'Huobi', None),
    ('Futures', 'Huobi', None),
    ('Spot', 'Okex', 'v3'),
    ('Futures', 'Okex', 'v3'),
    ('Futures', 'Okex', 'v3_realtime'),
    ('Futures', 'Okex', 'v3_swap'),
    ('Spot', 'Upbit', None),
    ('Spot', 'Kraken', None),
    ('Futures', 'Kraken', None),
    ('Spot', 'Quoinex', None),
    ('Spot', 'Quoinex', 'single_channel'),
    ('Spot', 'Hitbtc', None),
    ('Spot', 'Bequant', None),
    ('Spot', 'Gdax', None),
    ('Spot', 'Bitstamp', None),
    ('Spot', 'Korbit', None),
    ('Spot', 'Coinone', None),
    ('Spot', 'Gopax', None),
    ('Futures', 'Deribit', None),
    ('Futures', 'Bybit', None),
    ('Futures', 'Binance', None),
    ('Futures', 'Binance', 'l1_realtime'),
]


def launch(products,
           trading_date,
           machine,
           git_commit_datetime,
           git_commit_sha,
           sub_request,
           print_result=False,
           db_config=None):
  # UTC 0 hour
  start_time = datetime.datetime.combine(trading_date, datetime.time.min)
  hours = 24
  end_time = start_time + datetime.timedelta(hours=hours)

  feed_checker_config = get_default_feed_checker_config(products)

  strategy = DumpStrategy(machine,
                          products,
                          trading_date,
                          start_time,
                          end_time,
                          git_commit_datetime,
                          git_commit_sha,
                          sub_request)

  feed_sub_request = FeedSubscriptionRequest()
  feed_sub_request.add_products(products, sub_request)
  run_from_archive(feed_sub_request,
                   strategy.on_feed_reset,
                   start_time,
                   end_time,
                   machine=[machine],
                   feed_checker_config=feed_checker_config)

  if print_result:
    strategy.print_book_stats()
    print('')
    strategy.print_trade_stats()
    print('')
    strategy.print_cross_stats()
    print('')
  if db_config:
    strategy.dump_to_db(db_config)

  return 0


def main(_):
  start_date = FLAGS.start_date
  assert start_date, '--start_date must be specified.'
  end_date = FLAGS.end_date
  assert end_date, '--end_date must be specified.'

  start_date = convert_string_to_dates(start_date)[0]
  end_date = convert_string_to_dates(end_date)[0]
  trading_dates = [td for td in iterate_date(start_date, end_date)]
  db_config_file = FLAGS.db_config
  print_result = FLAGS.print_result
  max_workers = FLAGS.max_workers
  git_commit = FLAGS.git_commit
  with open(FLAGS.machine_list_file) as fd:
    machines = json.load(fd)

  git_commit_datetime, git_commit_sha = fetch_git_commit_sha_datetime(git_commit)
  for machine in machines:
    for trading_date in trading_dates:
      td_str = trading_date.strftime('%Y-%m-%d')
      print('\nRunning for %s %s ...' % (machine, td_str))
      with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
        future_product_list = []
        for market_type, exchange, api_version in EXCHANGE_LIST:
          products = get_exchange_product_list_grouped(market_type,
                                                       exchange,
                                                       api_version,
                                                       trading_date)
          sub_request = SubscriptionRequest(market_type, exchange, api_version)
          for product_group in products:
            product_group = to_list(product_group)
            future = executor.submit(launch,
                                     product_group,
                                     trading_date,
                                     machine,
                                     git_commit_datetime,
                                     git_commit_sha,
                                     sub_request,
                                     print_result,
                                     db_config_file)
            future_product_list.append((future, product_group))
        for future, product_group in future_product_list:
          try:
            future.result()
          except ValueError as e:
            print('Fail to get feed stats.',
                  machine,
                  td_str,
                  product_group[0].exchange,
                  product_group,
                  type(e),
                  e)
            if (product_group[0].exchange == 'Bitflyer'
                and product_group[0].symbol in ('BCH-BTC.IMMEDIATE', 'ETH-BTC.IMMEDIATE')):
              pass
            else:
              print(traceback.format_exc())
          except KeyboardInterrupt:
            print('Interrupted and quit by keyboard signal!')
            executor.shutdown()
            sys.exit(0)
          except Exception as e:
            print('Fail to get feed stats.',
                  machine,
                  td_str,
                  product_group[0].exchange,
                  product_group,
                  type(e),
                  e)
            print(traceback.format_exc())


if __name__ == '__main__':
  flags.DEFINE_string('start_date', None, 'yyyymmdd')

  flags.DEFINE_string('end_date', None, 'yyyymmdd. Feed at end_date is not checked.')

  flags.DEFINE_string('machine_list_file',
                      'coin/support/feed_tool/feed_stats/config/machine_list.json',
                      'list of machines whose feed to be checked.')

  flags.DEFINE_bool('print_result', False, '')

  flags.DEFINE_string('db_config', None, 'Database config file.')

  flags.DEFINE_integer('max_workers', 24, 'Max number of workers.')

  flags.DEFINE_string('git_commit', 'HEAD', 'Git commit sha or HEAD.')

  logging.basicConfig(level='DEBUG', format='%(levelname)8s %(asctime)s %(name)s] %(message)s')

  app.run(main)
